import time
from datetime import datetime

from pymongo import MongoClient
import numpy as np
from core.utils import Timer


class FeatureMaker:
    """A class for making feature to mongodb"""

    def __init__(self, ip):
        self.block_db = MongoClient('mongodb://%s:27017/' % ip)['bitcoinj_production']
        self.price_db = MongoClient('mongodb://%s:27018/' % ip)['exchange']
        self.feature_db = MongoClient('mongodb://%s:27019/' % ip)['features']

    def make_block_feature(self, end_timestamp, window):
        """
        Read block raw signal from database and convert to feature
        :param window:
        :param end_timestamp:
        :return: block feature
        """
        start_timestamp = end_timestamp - window * 60
        print('Get block feature. Start time: %s, end time: %s' % (start_timestamp, end_timestamp))
        timer = Timer()
        timer.start()
        block_list = self.block_db['block'].find({'time': {'$gt': start_timestamp, '$lte': end_timestamp}})

        block_tx = []
        for block in block_list:
            tx_list = self.block_db['tx'].find({'blockHash': block['_id']})
            for tx in tx_list:
                tx_total_value = self.block_db['output'].aggregate(
                    [{'$match': {'outTxId': tx['_id']}},
                     {'$group': {'_id': None, 'sum': {'$sum': '$value'}}}])
                total_value = float(list(tx_total_value)[0]['sum']) / 100000000
                block_tx.append(total_value)

        timer.stop()
        bins = 10
        hist, bin_edges = np.histogram([tx_value for tx_value in block_tx if tx_value < 100], bins=bins)
        hist = [int(e) for e in hist]
        out_threshold_tx_count = len([tx_value for tx_value in block_tx if tx_value >= 100])
        values = [len(block_tx), round(sum(block_tx), 5)]
        values.extend(hist)
        values.append(out_threshold_tx_count)
        keys = ['block_total_tx_count', 'block_total_value']
        keys.extend(['block-hist-' + str(e) for e in list(range(1, bins + 2, 1))])
        post = dict(zip(keys, values))
        post['_id'] = end_timestamp
        post['time'] = str(datetime.utcfromtimestamp(end_timestamp))
        print('insert to block_%s, entity: %s' % (window, post))
        self.feature_db['block_%s' % window].insert_one(post)

    def make_return_volatility(self, end_timestamp, window):
        """
        Read the price raw signal from the database and convert it to return and volatility
        :param window:
        :param end_timestamp:
        :return: return, volatility
        """
        start_timestamp = end_timestamp - window * 60
        print('Get return and volatility. Start time: %s, end time: %s' % (start_timestamp, end_timestamp))
        timer = Timer()
        timer.start()
        print('{\'_id\': {\'$gt\': %s, \'$lte\': %s}}' % (start_timestamp * 1000, end_timestamp * 1000))
        price_list = list(self.price_db['okex_btc_usdt'].find(
            {'_id': {'$gt': start_timestamp * 1000, '$lte': end_timestamp * 1000}}))
        if len(price_list) < window - 1:
            print('----Warning: Get price data less than actual number, need %s, only %s' %
                  (window, len(price_list)))
        print(len(price_list))
        total_price = []
        for res in price_list:
            total_price.append(res['close'])
        price_return = (total_price[-1] - total_price[0]) / total_price[0]
        price_volatility = np.std(total_price)
        timer.stop()
        post = {'_id': end_timestamp, 'real_return': price_return, 'real_volatility': price_volatility}
        print('insert to return_volatility_%s, entity: %s' % (window, post))
        self.feature_db['return_volatility_%s' % window].insert_one(post)


if __name__ == '__main__':
    window_size = 60
    end = time.time()
    feature_maker = FeatureMaker(ip='39.104.227.148')
    # feature_maker.make_block_feature(int(end), window_size)
    # Calculate return and volatility in the time window
    feature_maker.make_return_volatility(int(end), window_size)
